<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>简单饼图</title>
  <script type="text/javascript" src="http://d3js.org/d3.v5.min.js"></script>
</head>

<body>
  <svg width="800" height="600" style="background-color: gainsboro;"></svg>
</body>

<script>

  const svg = d3.select("svg");
  const width = svg.attr("width");
  const height = svg.attr("height");

  const margin = { left: 60, top: 60, right: 60, bottom: 60 };

  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const outerRadius = Math.min(innerWidth, innerHeight) / 2

  const maingroup = svg
    .append("g")
    .attr("transform", "translate(" + (innerWidth / 2 + margin.left) + "," + (margin.top + outerRadius) + ")")

  const data = [
    { age: '<5', population: 2704659 },
    { age: '5-13', population: 4499890 },
    { age: '14-17', population: 2159981 },
    { age: '18-24', population: 3853788 },
    { age: '25-44', population: 14106543 },
    { age: '45-64', population: 8819342 },
    { age: '≥65', population: 612463 }
  ]

  let colorScale = d3
    .scaleOrdinal()
    .range([
      '#98abc5',
      '#8a89a6',
      '#7b6888',
      '#6b486b',
      '#a05d56',
      '#d0743c',
      '#ff8c00'
    ])
    .domain(d3.map(data, d => d.age));


  /**
   * 饼图 - 扇形分区 + 动画
   */
  function arcTween(outerRadius, delay, that) {
    // 设置缓动函数
    d3.select(that)
      .transition()
      .delay(delay)
      .attrTween('d', function (d) {
        var i = d3.interpolate(d.outerRadius, outerRadius)
        return function (t) {
          d.outerRadius = i(t)
          return arc(d)
        }
      })
  }

  let _currentData = [];

  const arc = d3.arc()
    .innerRadius(outerRadius - 70)
    .outerRadius(outerRadius)
    .padAngle(0.03)

  const pie = d3.pie().sort(null).value(d => d.population);
  const arcData = pie(data);
  console.log("arcData", arcData);

  const gpie = maingroup.selectAll(".arc")
    .data(arcData)
    .join("g")

  gpie.append("path")
    .attr("curse", "pointer")
    .attr("class", "arc")
    .attr("stroke", "none")
    .attr("fill", (d, i) => {
      return colorScale(d.data.age)
    })
    .each(function (d) {
      // 储存当前起始与终点的角度、并设为相等
      let tem = { ...d, endAngle: d.startAngle }
      // d.outerRadius = outerRadius - 10
      // _currentData的作用，
      // 动画的效果是将每个扇形慢慢展开
      // 所以初始状态是：扇形不展开，endAngle: d.startAngle
      _currentData.push(tem)
    })
    .on('mouseover', function () {
      // console.log("m");
      // arcTween(outerRadius + 20, 0, this)
      d3.select(this)
        .transition()
        .delay(0)
        .attrTween('d', function (d) {
          var i = d3.interpolate(d.outerRadius, outerRadius + 20)
          return function (t) {
            d.outerRadius = i(t)
            return arc(d)
          }
        })
    })
    .on('mouseout', function () {
      arcTween(outerRadius - 10, 150, this)
    })
    .transition()
    .duration(100) //持续
    .delay(function (d, i) { //延迟
      return i * 100
    })
    .attrTween("d", (next, i) => {     // next为d
      // console.log("_currentData:", _currentData[i]);
      // console.log("next:", next);
      // 插值器动态设置d属性、生成动画
      let v = d3.interpolate(_currentData[i], next);
      // _currentData = v(0) 
      return function (t) {
        return arc(v(t))
      }
    })


  /**
   * 饼图上添加数字1
   */
  let percentLabelArc = d3
    .arc() // 定义单个圆弧里面的percent文字
    .outerRadius(outerRadius - 20)
    .innerRadius(outerRadius - 20)

  let ageLabelArc = d3
    .arc() // 定义单个圆弧里面的percent文字
    .outerRadius(outerRadius - 70)
    .innerRadius(outerRadius - 70)

  const total = d3.sum(data, d => d.population)

  gpie.append("text")
    .attr('fill', '#cddc39')
    .attr('font-weight', '700')
    .attr('font-size', '14px')
    .attr('text-anchor', 'start')
    .attr("transform", function (d) {//位置设在中心处
      return "translate(" + percentLabelArc.centroid(d) + ")";
    })
    .text(d => {
      return ((d.value / total) * 100).toFixed(1) + "%"
    })

  gpie.append("text")
    .attr('fill', '#000')
    .attr('font-weight', '700')
    .attr('font-size', '14px')
    .attr('text-anchor', 'middle')
    .attr("transform", function (d) {//位置设在中心处
      return "translate(" + ageLabelArc.centroid(d) + ")";
    })
    .text(d => {
      return d.data.age + "岁"
    })


  /**
   *  外提示文本
   */
  let populationLabelArc = d3
    .arc() // 定义单个圆弧里面的population文字
    .outerRadius(outerRadius + 20)
    .innerRadius(outerRadius + 20)

  let glinkLabel = maingroup.append('g').attr("class", "line-label-g")

  glinkLabel.selectAll()
    .data(arcData)
    .enter()
    .append("text")
    .attr("x", (d, i) => populationLabelArc.centroid(d)[0])
    .attr("y", (d, i) => populationLabelArc.centroid(d)[1])
    .attr("fill", "#000")
    .attr('text-anchor', function (d, i) {
      return populationLabelArc.centroid(d)[0] >= 0 ? 'start' : 'end'
    })
    .attr("font-size", "12px")
    .text(d => {
      return (d.data.population / 10000).toFixed(2) + "万人"
    })

  /**
   * 饼图中心添加标题
   */
  maingroup.append("g")
    .attr("class", 'pie-title')
    .append("text")
    .attr("fill", "#000")
    .attr("text-anchor", 'middle')
    .attr("font-size", '14px')
    .attr("font-weight", '700')
    .text("XX市人口年龄结构")



</script>

</html>
